﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace Pulse
{
    class MapRow
    {
        public List<Cell> columns = new List<Cell>();
    }

    class Map
    {
        public List<MapRow> rows = new List<MapRow>();
        public int mapWidth;
        public int mapHeight;

        public int squaresAcross = 12;
        public int squaresDown = 30;

        float heightRowDepthMod = 0.0000001f;
        float maxdepth;
        float depthOffset;
        public float playerDrawDepth;

        private Texture2D mouseMap;
        public Texture2D highlight;

        public Vector2 cursorPosition = Vector2.Zero;
        public Vector2 screenSize = Vector2.Zero;

        /* The tile coordinates of the cell the player is stand on */
        public Point playerMapCell;

        /* An array intended for use with the mouse map */
        private Color[] myColor;

        public Map(int width, int height, Vector2 size)
        {
            mapWidth = width;
            mapHeight = height;
            screenSize = size;

            maxdepth = ((mapWidth + 1) + ((mapHeight + 1) * Tile.tileWidth)) * 10;

            for (int y = 0; y < mapHeight; y++)
            {
                MapRow thisRow = new MapRow();
                for (int x = 0; x < mapWidth; x++)
                {
                    thisRow.columns.Add(new Cell(0));
                }
                rows.Add(thisRow);
            }

            createSampleTerrain();
        }

        public void LoadMouseMap(Texture2D mousemap, Texture2D hilight)
        {
            mouseMap = mousemap;
            highlight = hilight;

            myColor = new Color[mousemap.Width * mousemap.Height];
            mousemap.GetData(myColor);
        }

        public Vector2 WorldToScreen(Vector2 worldPosition, Vector2 Location)
        {
            return worldPosition - Location + screenSize/2;
        }

        public Vector2 ScreenToWorld(Vector2 screenPosition, Vector2 Location)
        {
            return screenPosition + Location - screenSize/2;
        }

        /* Given a world position, find the cell that the position is above */
        public Point WorldToMapCell(Point worldPoint, out Point localPoint)
        {
            Point mapCell = new Point(
               (int)(worldPoint.X / mouseMap.Width),
               ((int)(worldPoint.Y / mouseMap.Height)) * 2
               );

            int localPointX = worldPoint.X % mouseMap.Width;
            int localPointY = worldPoint.Y % mouseMap.Height;
            
            int dx = 0;
            int dy = 0;

            int index = 0;

            Rectangle mouseRect = new Rectangle(0, 0, mouseMap.Width, mouseMap.Height);
            if (mouseRect.Contains(localPointX, localPointY))
            {
                index = (localPointY * Tile.tileWidth) + localPointX;

                if (myColor[index] == Color.Red) // Red
                {
                    dx = -1;
                    dy = -1;
                    localPointX = localPointX + (mouseMap.Width / 2);
                    localPointY = localPointY + (mouseMap.Height / 2);
                }
                else if (myColor[index] == Color.Green) // Green
                {
                    dx = -1;
                    localPointX = localPointX + (mouseMap.Width / 2);
                    dy = 1;
                    localPointY = localPointY - (mouseMap.Height / 2);
                }
                else if (myColor[index] == Color.Yellow) // Yellow
                {
                    dy = -1;
                    localPointX = localPointX - (mouseMap.Width / 2);
                    localPointY = localPointY + (mouseMap.Height / 2);
                }
                else if (myColor[index] == Color.Blue) // Blue
                {
                    dy = +1;
                    localPointX = localPointX - (mouseMap.Width / 2);
                    localPointY = localPointY - (mouseMap.Height / 2);
                }
            }

            mapCell.X += dx;
            mapCell.Y += dy - 2;

            localPoint = new Point(localPointX, localPointY);

            return mapCell;
        }

        public Point WorldToMapCell(Point worldPoint)
        {
            Point dummy;
            return WorldToMapCell(worldPoint, out dummy);
        }

        public Point WorldToMapCell(Vector2 worldPosition)
        {
            Point dummy;
            return WorldToMapCell(new Point((int)worldPosition.X, (int)worldPosition.Y), out dummy);
        }

        public Cell GetCell(int x, int y)
        {
            int newX = x;
            int newY = y;

            if (x < 0)
                newX = 0;
            else if (x >= mapWidth)
                newX = mapWidth - 1;

            if (y < 0)
                newY = 0;
            else if (y >= mapHeight)
                newY = mapHeight - 1;

            return rows[newY].columns[newX];
        }

        public Cell GetCell(Point cell)
        {
            return GetCell(cell.X, cell.Y);
        }

        public Cell GetCell(Vector2 position)
        {
            return GetCell(WorldToMapCell(new Point((int)position.X, (int)position.Y)));
        }

        public int GetTileHeight(int x, int y)
        {
            if (x >= mapWidth || y >= mapHeight
                || x < 0 || y < 0)
                return 0;

            return rows[y].columns[x].Height;
        }

        public int GetTileHeight(Point cell)
        {
            return GetTileHeight(cell.X, cell.Y);
        }

        public int GetTileHeight(Vector2 position)
        {
            return GetTileHeight(WorldToMapCell(new Point((int)position.X, (int)position.Y)));
        }

        // return the draw order of the "highest" cell element.
        public float GetTopDrawOrder(int x, int y)
        {
            Vector2 firstSquare = Vector2.Zero;
            int firstX = (int)firstSquare.X;
            int firstY = (int)firstSquare.Y;

            int rowOffset = 0;
            if ((firstY + y) % 2 == 1)
                rowOffset = Tile.OddRowXOffset;

            int mapx = (firstX + x);
            int mapy = (firstY + y);
            depthOffset = 0.7f - ((mapx + (mapy * Tile.tileWidth)) / maxdepth);

            int heightRow = GetTileHeight(x, y) - 1;

            if ((mapx >= mapWidth) || (mapy >= mapHeight))
                return -1.0f;

            return (depthOffset - (float)(heightRow + 2) * heightRowDepthMod);
        }

        public void Update(GameTime gametime, MouseState ms, Vector3 camPos)
        {
            Vector2 mousePosition = new Vector2(ms.X, ms.Y);
            Vector2 camPosition = new Vector2(camPos.X, camPos.Y);
            cursorPosition = ScreenToWorld(camPosition, mousePosition);
        }

        public void DrawGround(SpriteBatch spriteBatch)
        {
            
            /* Which upper-left tile is drawn first, default is the 0,0 tile */
            Vector2 firstSquare = Vector2.Zero;
            int firstX = (int)firstSquare.X;
            int firstY = (int)firstSquare.Y;

            for (int y = 0; y < squaresDown; y++)
            {
                int rowOffset = 0;
                if ((firstY + y) % 2 == 1)
                    rowOffset = Tile.OddRowXOffset;

                for (int x = 0; x < squaresAcross; x++)
                {
                    int mapx = (firstX + x);
                    int mapy = (firstY + y);
                    depthOffset = 0.7f - ((mapx + (mapy * Tile.tileWidth)) / maxdepth);

                    if ((mapx >= mapWidth) || (mapy >= mapHeight))
                        continue;

                    /* Draw the base tiles, the terrain */
                    foreach (int tileID in rows[mapy].columns[mapx].baseTiles)
                    {
                        spriteBatch.Draw(

                            Tile.tileSet,
                            new Rectangle(
                                (x * Tile.TileStepX) + rowOffset,
                                (y * Tile.TileStepY),
                                Tile.tileWidth, Tile.tileHeight),
                            Tile.GetSourceTile(tileID),
                            Color.White,
                            0.0f,
                            Vector2.Zero,
                            SpriteEffects.None,
                            1.0f);
                    }
                }
            }
        }

        public void DrawHeightTiles(SpriteBatch spriteBatch, Vector2 playerPos)
        {
            
            /* Which upper-left tile is drawn first, default is the 0,0 tile */
            Vector2 firstSquare = Vector2.Zero;
            int firstX = (int)firstSquare.X;
            int firstY = (int)firstSquare.Y;

            playerMapCell = WorldToMapCell(playerPos);

            for (int y = 0; y < squaresDown; y++)
            {
                int rowOffset = 0;
                if ((firstY + y) % 2 == 1)
                    rowOffset = Tile.OddRowXOffset;

                for (int x = 0; x < squaresAcross; x++)
                {
                    int mapx = (firstX + x);
                    int mapy = (firstY + y);
                    depthOffset = 0.7f - ((mapx + (mapy * Tile.tileWidth)) / maxdepth);

                    if ((mapx >= mapWidth) || (mapy >= mapHeight))
                        continue;

                    /* Draw the height tiles/blocks that are placed above ground */
                    int heightRow = 0;

                    foreach (int tileID in rows[mapy].columns[mapx].heightTiles)
                    {
                        spriteBatch.Draw(
                            Tile.tileSet,
                            new Rectangle(
                                (x * Tile.TileStepX) + rowOffset,
                                (y * Tile.TileStepY) - (heightRow * Tile.HeightTileOffset),
                                Tile.tileWidth, Tile.tileHeight),
                            Tile.GetSourceTile(tileID),
                            Color.White,
                            0.0f,
                            Vector2.Zero,
                            SpriteEffects.None,
                            depthOffset - ((float)heightRow * heightRowDepthMod));
                        heightRow++;
                    }

                    /* Draw the layer tiles */
                    foreach (int tileID in rows[y + firstY].columns[x + firstX].layerTiles)
                    {
                        spriteBatch.Draw(
                            Tile.tileSet,
                            new Rectangle(
                                (x * Tile.TileStepX) + rowOffset,
                                (y * Tile.TileStepY) - (heightRow * Tile.HeightTileOffset),
                                Tile.tileWidth, Tile.tileHeight),
                            Tile.GetSourceTile(tileID),
                            Color.White,
                            0.0f,
                            Vector2.Zero,
                            SpriteEffects.None,
                            depthOffset - ((float)heightRow * heightRowDepthMod));
                    }

                    if ((mapx == playerMapCell.X) && (mapy == playerMapCell.Y))
                    {
                        //float tempOffset = depthOffset;
                        //depthOffset = 0.7f - ((mapx + ((mapy) * Tile.tileWidth)) / maxdepth);
                        //playerDrawDepth = GetLowestDrawOrderAtHeight(playerMapCell, heightRow) - (float)(heightRow + 2) * heightRowDepthMod;
                        playerDrawDepth = depthOffset - (float)(heightRow + 2) * heightRowDepthMod;
                        //depthOffset = tempOffset;
                    }
                }
            }
        }

        public void Draw(SpriteBatch spriteBatch, Vector2 playerPos)
        {
            /* Which upper-left tile is drawn first, default is the 0,0 tile */
            Vector2 firstSquare = Vector2.Zero;
            int firstX = (int)firstSquare.X;
            int firstY = (int)firstSquare.Y;

            playerMapCell = WorldToMapCell(playerPos);

            for (int y = 0; y < squaresDown; y++)
            {
                int rowOffset = 0;
                if ((firstY + y) % 2 == 1)
                    rowOffset = Tile.OddRowXOffset;

                for (int x = 0; x < squaresAcross; x++)
                {
                    int mapx = (firstX + x);
                    int mapy = (firstY + y);
                    depthOffset = 0.7f - ((mapx + (mapy * Tile.tileWidth)) / maxdepth);

                    if ((mapx >= mapWidth) || (mapy >= mapHeight))
                        continue;

                    /* Draw the base tiles, the terrain */
                    foreach (int tileID in rows[mapy].columns[mapx].baseTiles)
                    {
                        spriteBatch.Draw(

                            Tile.tileSet,
                            new Rectangle(
                                (x * Tile.TileStepX) + rowOffset,
                                (y * Tile.TileStepY),
                                Tile.tileWidth, Tile.tileHeight),
                            Tile.GetSourceTile(tileID),
                            Color.White,
                            0.0f,
                            Vector2.Zero,
                            SpriteEffects.None,
                            1.0f);
                    }

                    /* Draw the height tiles/blocks that are placed above ground */
                    int heightRow = 0;

                    foreach (int tileID in rows[mapy].columns[mapx].heightTiles)
                    {
                        spriteBatch.Draw(
                            Tile.tileSet,
                            new Rectangle(
                                (x * Tile.TileStepX) + rowOffset,
                                (y * Tile.TileStepY) - (heightRow * Tile.HeightTileOffset),
                                Tile.tileWidth, Tile.tileHeight),
                            Tile.GetSourceTile(tileID),
                            Color.White,
                            0.0f,
                            Vector2.Zero,
                            SpriteEffects.None,
                            depthOffset - ((float)heightRow * heightRowDepthMod));
                        heightRow++;
                    }

                    /* Draw the layer tiles */
                    foreach (int tileID in rows[y + firstY].columns[x + firstX].layerTiles)
                    {
                        spriteBatch.Draw(
                            Tile.tileSet,
                            new Rectangle(
                                (x * Tile.TileStepX) + rowOffset,
                                (y * Tile.TileStepY) - (heightRow * Tile.HeightTileOffset),
                                Tile.tileWidth, Tile.tileHeight),
                            Tile.GetSourceTile(tileID),
                            Color.White,
                            0.0f,
                            Vector2.Zero,
                            SpriteEffects.None,
                            depthOffset - ((float)heightRow * heightRowDepthMod));
                    }

                    if ((mapx == playerMapCell.X) && (mapy == playerMapCell.Y))
                    {
                        //float tempOffset = depthOffset;
                        //depthOffset = 0.7f - ((mapx + ((mapy) * Tile.tileWidth)) / maxdepth);
                        //playerDrawDepth = GetLowestDrawOrderAtHeight(playerMapCell, heightRow) - (float)(heightRow + 2) * heightRowDepthMod;
                        playerDrawDepth = depthOffset - (float)(heightRow + 2) * heightRowDepthMod;
                        //depthOffset = tempOffset;
                    }
                }
            }

        }

        public void createSampleTerrain()
        {
            // Create Sample Map Data
            rows[16].columns[4].AddHeightTile(54);

            rows[17].columns[3].AddHeightTile(54);

            rows[15].columns[3].AddHeightTile(54);
            rows[16].columns[3].AddHeightTile(53);

            rows[15].columns[4].AddHeightTile(54);
            rows[15].columns[4].AddHeightTile(54);
            rows[15].columns[4].AddHeightTile(51);

            rows[18].columns[3].AddHeightTile(51);
            rows[19].columns[3].AddHeightTile(50);
            rows[18].columns[4].AddHeightTile(55);

            rows[14].columns[4].AddHeightTile(54);

            rows[14].columns[5].AddHeightTile(62);
            rows[14].columns[5].AddHeightTile(61);
            rows[14].columns[5].AddHeightTile(63);

            rows[17].columns[4].AddLayerTile(67);
            rows[16].columns[5].AddLayerTile(66);
            rows[14].columns[4].AddLayerTile(125);
            rows[15].columns[5].AddLayerTile(91);
            rows[16].columns[6].AddLayerTile(94);

            rows[15].columns[5].Walkable = false;
            rows[16].columns[6].Walkable = false;

            ////rows[17].columns[4].AddHeightTile(54);
            ////rows[17].columns[5].AddHeightTile(54);
            ////rows[17].columns[6].AddHeightTile(54);
            ////rows[17].columns[7].AddHeightTile(54);
            ////rows[17].columns[8].AddHeightTile(54);
            ////rows[17].columns[9].AddHeightTile(54);
            ////rows[17].columns[10].AddHeightTile(54);

            ////rows[18].columns[4].AddHeightTile(54);
            ////rows[18].columns[5].AddHeightTile(54);
            ////rows[18].columns[6].AddHeightTile(54);
            ////rows[18].columns[7].AddHeightTile(54);
            ////rows[18].columns[8].AddHeightTile(54);
            ////rows[18].columns[9].AddHeightTile(54);
            ////rows[18].columns[10].AddHeightTile(54);
            
            ////rows[19].columns[4].AddHeightTile(54);
            ////rows[19].columns[5].AddHeightTile(54);
            ////rows[19].columns[6].AddHeightTile(54);
            ////rows[19].columns[7].AddHeightTile(54);
            ////rows[19].columns[8].AddHeightTile(54);
            ////rows[19].columns[9].AddHeightTile(54);
            ////rows[19].columns[10].AddHeightTile(54);

            ////rows[20].columns[4].AddHeightTile(54);
            ////rows[20].columns[5].AddHeightTile(54);
            ////rows[20].columns[6].AddHeightTile(54);
            ////rows[20].columns[7].AddHeightTile(54);
            ////rows[20].columns[8].AddHeightTile(54);
            ////rows[20].columns[9].AddHeightTile(54);
            ////rows[20].columns[10].AddHeightTile(54);

            ////rows[21].columns[4].AddHeightTile(54);
            ////rows[21].columns[5].AddHeightTile(54);
            ////rows[21].columns[6].AddHeightTile(54);
            ////rows[21].columns[7].AddHeightTile(54);
            ////rows[21].columns[8].AddHeightTile(54);
            ////rows[21].columns[9].AddHeightTile(54);
            ////rows[21].columns[10].AddHeightTile(54);

        }
    }
}
